#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <semaphore.h>
#include <string.h>
#include <input_manager.h>


static struct InputDevice *g_InputDevs = NULL; // 输入设备链表
static pthread_mutex_t g_tMutex  = PTHREAD_MUTEX_INITIALIZER; // 互斥锁
static pthread_cond_t  g_tConVar = PTHREAD_COND_INITIALIZER; // 条件变量


// 实现环形缓冲区
#define BUFFER_LEN 20
static int iR = 0; 
static int iW = 0; // 表示环形缓冲区的读写指针

// g表示全局变量，a表示array，t表示结构体
static InputEvent g_atInputEvents[BUFFER_LEN]; // g_atInputEvents是一个能够存放多个InputEvent的数组，也就是存放InputEvent的环形缓冲区

// 判断环形缓冲区是否没空（还有可读取的数据）
static int IsCircleBufferEmpty(void)
{
    if (iR == iW)
    {
        return 1;  // 缓冲区空
    }
    else
    {
        return 0;  // 还有数据可读
    }
}

// 判断环形缓冲区是否没满（还有可写入的空间）
static int IsCircleBufferFull(void)
{
    if ((iW + 1) % BUFFER_LEN == iR)
    {
        return 1;  // 缓冲区满
    }
    else
    {
        return 0;  // 还有空间可写
    }
}

// 向环形缓冲区g_atInputEvents中写入PInputEvent的数据
static int WriteToCircleBuffer(PInputEvent ptInputEvent)
{
	// 如果缓冲区还没满（还有可以写入的空间），则将ptInputEvent中的数据写入g_atInputEvents
	int ret;
	ret = IsCircleBufferFull();
	if (ret == 0)
	{
		/* 向缓冲区中写入数据 */
		g_atInputEvents[iW] = *ptInputEvent;  // 拷贝结构体内容
        iW = (iW + 1) % BUFFER_LEN;           // 写指针前进（注意环绕）
	}
	else
	{
		printf("Circle Buffer is full!\n");
		printf("No place to write in.\n");
		return -1; // 写入失败，返回-1
	}
	return 0;
}

// 从环形缓冲区g_atInputEvents中读取数据存到ptInputEvent中
static int ReadFromCircleBuffer(PInputEvent ptInputEvent)
{
	// 如果缓冲区还没空（还有可以读取的数据）则从g_atInputEvents读取数据保存到ptInputEvent中
	int ret;
	ret = IsCircleBufferEmpty();
	if (ret == 0)
	{
		/* 从缓冲区中读取数据 */
		*ptInputEvent = g_atInputEvents[iR];
        iR = (iR + 1) % BUFFER_LEN; 
	}
	else
	{
		printf("Circle Buffer is Empty!\n");
		printf("No data to read from.\n");
		return -1; // 读取失败，返回-1
	}
	return 0; // 返回0表示成功
}

// 注册单个输入设备到设备链表中
int RegisterInputDevice(InputDevice *ptInputDevice)
{
	ptInputDevice->ptNext = g_InputDevs;
    g_InputDevs = ptInputDevice;
	return 0;
}

// 注册所有输入设备
int RegisterAllInputDevs(void)
{	
	// 注册NI设备
	extern void RegisterNItoDeviceList(void);
	RegisterNItoDeviceList();

	// 注册TS设备
	extern void RegisterTStoDeviceList(void);
	RegisterTStoDeviceList();

	return 0;
}


/*
	input_recv_thread_func 就是线程要做的“工作”
	data 就是这个线程需要处理的“信息”，可以是任何类型的数据（比如设备信息、事件数据等）
	当你创建线程时，传入的 data 让线程知道 它需要做什么，它就可以根据 data 来进行相应的操作
*/


/*
	每个输入设备对应一个线程，线程在等待数据时是 休眠的
	当一个线程（设备）接收到数据并处理完后，它 唤醒 其他线程，表示数据已准备好，其他线程可以开始工作
	唤醒后，其他线程会继续执行，然后再次进入 等待状态，直到下一次数据到来
*/

static void *input_recv_thread_func(void *data)
{	
	PInputDevice ptInputDev = (PInputDevice)data;
	InputEvent tEvent;  // 声明一个InputEvent结构体变量tEvent
	int ret;

	// 不断从输入设备获取数据，一旦有数据就将数据写入缓冲区并唤醒其他等待数据的线程
	while (1)
	{	
		// 获取输入事件（填充tEvent）
		// GetInputEvent会根据InputEvent的类型（是触屏输入还是网络输入）调用不同的获取输入事件的函数
		ret = ptInputDev->GetInputEvent(&tEvent);

		if (ret == 0)  // 获取成功
		{	
			// 上锁
			pthread_mutex_lock(&g_tMutex);
			
			// 写入环形缓冲区
			WriteToCircleBuffer(&tEvent);
			
			// 唤醒等待数据的线程（通常是主线程或其他读取线程）
			pthread_cond_signal(&g_tConVar);
			
			// 解锁
			pthread_mutex_unlock(&g_tMutex);
		}
		
		// 失败就略过，继续下一轮
	}

	return NULL; // 这一句一定要写啊！！因为这个函数的返回类型是void*而不是void！！
}


// 初始化输入设备（为链表中的每个输入设备创建线程）
int InputDevsInit(void)
{
	pthread_t tid;
	int ret;
	PInputDevice pInputDev_tmp; // pInputDev_tmp作为临时指针，用来遍历链表中的每一个输入设备

	pInputDev_tmp = g_InputDevs;
	while (pInputDev_tmp)
	{
		// InputDeviceInit会根据PInputDevice的类型（是触屏输入设备还是网络输入设备）调用不同的初始化函数
		ret = pInputDev_tmp->InputDeviceInit();
		
		// 创建线程
		if (!ret) // 也就是ret返回0，即!ret为1，说明初始化成功，这样才为输入设备创建线程（看初始化代码应该是成功返回0，失败返回-1）
		{
			ret = pthread_create(&tid, NULL, input_recv_thread_func, (void *)pInputDev_tmp);
			// 创建线程失败，返回错误码
			if (ret != 0)
            {
                printf("Failed to create thread for device %s\n", pInputDev_tmp->name);
                return -1;
            }
		}
		pInputDev_tmp = pInputDev_tmp->ptNext; // 处理下一个输入设备
	}
	return 0;
}



// 获取输入事件存放到ptInputEvent中
// 如果缓冲区暂时没有数据（为空），那就先等待，等写线程写入数据后被唤醒，再去读取数据，然后再解锁退出
int GetInputEvent(PInputEvent ptInputEvent)
{
	int ret;

	// 上锁，确保不会和其他线程冲突
    pthread_mutex_lock(&g_tMutex);
    // 缓冲区为空说明没数据可以读取，线程休眠等待数据到来
    while (IsCircleBufferEmpty())
    {	
		// 既然没有数据读取，那就先释放锁，让其他的线程可以写入数据；其他线程写入数据之后会通知当前的线程，重新加锁并且读数据
        pthread_cond_wait(&g_tConVar, &g_tMutex);  
    }
    // 缓冲区非空说明有数据可以读取，则调用ReadFromCircleBuffer读数据放到ptInputEvent中
    ret = ReadFromCircleBuffer(ptInputEvent);
	// 解锁
    pthread_mutex_unlock(&g_tMutex);

    return ret;  // 返回0表示读取成功，-1表示失败

}